這個問題其實在 Day8 的文章有稍微提到過,但大多數人看文件時都大致看一下而會忽略一些小細節,包含我也是 XD。且 Livewire 官方文件並沒有清楚列出避免這個問題的方法。因此再拉出來講解,希望能幫助到遇到這個問題的人。
在使用 $emit('foo')
觸發後端的 Listener 的同時也會觸發到前端的 Livewire.on('foo'),可以理解是註冊成一個監聽。
註:不管是在頁面按鈕上的
wire:click="$emit('foo')"
或是後端的$this->emit('foo')
都是一樣的!
<script>
Livewire.on('foo', () => {
...
})
</script>
先來看一下錯誤訊息:
Uncaught ReferenceError: Livewire is not defined
嗯嗯,看起來是沒有定義 Livewire 呢!那為什麼會沒有定義呢,不是外面早就引入過 @livewireScripts
了嗎?
照官方文件給的 Layout佈局 會像是這樣子:
<head>
@livewireStyles
</head>
<body>
{{ $slot }}
@livewireScripts
</body>
寫在 Livewire元件 中的程式碼都會被填在 {{ $slot }}
的位置,因此等於在 JavaScript 還沒引入 Livewire 時就先用到了 Livewire,從而導致出錯。
知道問題後要解決也很簡單。
@livewireScripts
提到 {{ $slot }}
上面。不過不是推薦,因為還是建議把 JS 都放在 <Body>
的最下面。
<body>
@livewireScripts
{{ $slot }}
</body>
或是使用 @yield 跟 @section,這兩者差別是在於 @stack/push 可以堆棧多組來自不同 components 的內容。而 @yield/section 只能用一次。為了日後有可能會用到導致要改就要全部改,不如一開始就直接用 @stack
<body>
{{ $slot }}
@livewireScripts
@stack('scripts')
</body>
在 Livewire Blade 內:
<div>
<button wire:click="$emit('foo')">CLICK</button>
</div>
@push('scripts')
<script>
Livewire.on('foo', () => {
...
})
</script>
@endpush
結論: @livewireScripts 引入的位置要比你的
<script>
還上面